#include "Utils.h"
#include <iostream>
#include <ctime>

#define ENABLE_LOG 0
#define INIT_STEP 0
#define ADD_3_STEP 1
#define ADD_2_STEP 2
#define ADD_4 0
#define ADD_2 1
#define FOUND_3_FIRST 0
#define FOUND_4_FIRST 1
UtilsPtr Utils::m_Instance;

Utils::Utils()
{
}

Utils::~Utils()
{
}

UtilsPtr Utils::GetInstance()
{
	if (!m_Instance)
	{
		m_Instance = UtilsPtr(new Utils());
	}
	return m_Instance;
}

//intialize
void Utils::Initialize()
{
	time_t startt, endt;
	time(&startt);
#if ENABLE_LOG
	std::cout << "Init" << std::endl;
#endif
	m_algorithm = FOUND_3_FIRST;
	m_battle = BattleShipPtr(new BattleShip());
	m_state = BEGIN;
	m_step = 0;
	m_swap = (rand() % 2) == 0;
#if ENABLE_LOG
	std::cout << "m_swap: " <<m_swap<<std::endl;
#endif
	for (int i=0;i<MAX_SHIP;i++)
		m_foundShip[i] = false;
	m_maxLength = 5;
	m_minLength = 2;
	m_checkPoint.clear();
	m_pendingPoint.clear();
	m_lateTwo = false;
	AddMorePoint();
#if ENABLE_LOG
	std::cout << "Size:" << m_checkPoint.size() << std::endl;
#endif

	time(&endt);
#if ENABLE_LOG	
	std::cout << "Init done" << difftime(endt,startt) << std::endl;
#endif
	//m_thread = new ThreadType(Utils::ThreadRunning);
}//end


//guess a position
Point Utils::GuessPos()
{
	Point result = Point(0,0);
	float max_result = 0;
	switch (m_state) {
		case BEGIN:
		case FINDING:
			{
				//clean up before guess pos
				CleanUp();

				//add more point if needed
				if (m_checkPoint.size() == 0)
				{
					AddMorePoint();
				}

				RecheckPoint();

				int i = rand() % m_checkPoint.size();
				result = m_checkPoint[i];
				if (!m_battle->IsUnkown(result))
				{
					std::cout << "ERROR, We known this point"<<std::endl;
				}
				break;
			}
		case FOUND_ONE:
			{
				//clean up before guess pos
				//CleanUp(); //this also need clean up

				for (int i=0;i <4;i++)
				{
					Point newP;
					newP.first = m_lastPoint.first + s_dx[i];
					newP.second = m_lastPoint.second + s_dy[i];
					if (m_battle->IsUnkown(newP))
					{
						//simple first, optimize later
						result = newP;
						if (i==1 || i==3)
						{
							m_direction = Ship::VERTICAL;
						}
						else
						{
							m_direction = Ship::HORIZONTAL;
						}
						break;
					}
				}

                if (!m_battle->IsUnkown(result))
				{
					std::cout << "ERROR, We known this point"<<std::endl;
				}

				break;
			}
		case FOUND_TWO:
			{
				//clean up before guess pos
				//CleanUp(); //not sure, this need clean up

				int k = 0;
				if (m_direction == Ship::VERTICAL)
				{
					k = 1;
				}
				Point newP1 = m_lastPoint;
				Point newP2 = m_lastPoint;
				while (true)
				{
					if (m_battle->IsInShip(newP1))
					{
						newP1.first = newP1.first + s_dx[k];
						newP1.second = newP1.second + s_dy[k];
						if (m_battle->IsUnkown(newP1))
						{
							result = newP1;
							break;
						}
					}

					if (m_battle->IsInShip(newP2))
					{
						newP2.first = newP2.first - s_dx[k];
						newP2.second = newP2.second - s_dy[k];
						if (m_battle->IsUnkown(newP2))
						{
							result = newP2;
							break;
						}
					}
				}

                if (!m_battle->IsUnkown(result))
				{
					std::cout << "ERROR, We known this point"<<std::endl;
				}

				break;
			}
		default:
			break;
	}
#if ENABLE_LOG	
	std::cout << "Guess " << result.first << ":"<<result.second << std::endl;
#endif
	return result;
}//end

void Utils::ProcessPos(Point p, bool value)
{
#if ENABLE_LOG	
	std::cout << "Found " << p.first << ":" << p.second << ":"<< value <<std::endl;
#endif
	PointArray::iterator ii;
	for (ii=m_checkPoint.begin();ii!=m_checkPoint.end();ii++)
	{
		if (*ii == p)
		{
			break;
		}
	}
	if (ii != m_checkPoint.end())
	{
		m_checkPoint.erase(ii);
	}

	// remove from pending point
	for (ii = m_pendingPoint.begin(); ii != m_pendingPoint.end(); ii++)
	{
		if (*ii == p)
		{
			break;
		}
	}

	if (ii != m_pendingPoint.end())
	{
		m_pendingPoint.erase(ii);
	}
	//end

	m_battle->SetValue(p,value);
	if (value)
	{

		switch (m_state) {
		case BEGIN:
		case FINDING:
			m_lastPoint = p;
			m_state = FOUND_ONE;
			break;
		case FOUND_ONE:
			m_state = FOUND_TWO;
			break;
		case FOUND_TWO:
			break;
		default:
			std::cout << "ERROR" << std::endl;
			break;
		}
	}
	else
	{
		switch (m_state) {
		case BEGIN:
		case FINDING:
		case FOUND_ONE:
		case FOUND_TWO:
			break;
		default:
			std::cout << "ERROR" << std::endl;
			break;
		}
	}
}

//prepare for new set
void Utils::EndSet()
{
}//end

//found a ship, process it
void Utils::FoundShip(Point p, Ship::TypeOfShip _type)
{
#if ENABLE_LOG	
	std::cout << "Found ship: %d" << _type << std::endl;
#endif
	m_state = FINDING;
	ShipPtr ship = m_battle->FindShip(p,_type);
	if (ship)
	{
		m_foundShip[_type] = true;
		for (int i=Ship::AIRCRAFT; i >= Ship::DESTROYER;i--)
		{
			if (!m_foundShip[i])
			{
				m_maxLength = Ship::GetLength((Ship::TypeOfShip)i);
				break;
			}
		}
		//recheck m_minLength
		for (int i=Ship::DESTROYER; i <= Ship::AIRCRAFT;i++)
		{
			if (!m_foundShip[i])
			{
				m_minLength = Ship::GetLength((Ship::TypeOfShip)i);
				break;
			}
		}
		if (m_minLength == m_maxLength && m_minLength == 2)
		{
			m_lateTwo = true;
		}
		/*
		if (m_algorithm == FOUND_4_FIRST)
		{
			if (m_maxLength < 4)
			{
				AddMorePoint();
			}
		}
		*/
		//remove outside points
		PointSet pointSet = ship->GetSpaceForShip();
		PointSet::iterator ii;
		for (ii = pointSet.begin(); ii != pointSet.end(); ii++)
		{
			if (m_battle->IsUnkown(*ii))
			{
				ClearPos(*ii);
			}
		}
	} 
	else
	{
		std::cout << "CLGT, we can't find ship" << std::endl;
	}
}
//end

void Utils::AddMorePoint()
{
	if (m_algorithm == FOUND_4_FIRST)
	{
		//algorithm 1
		if (m_step == ADD_4)
		{
			m_checkPoint.clear();
			for (int i=0;i<MAX_H;i++)
			for (int j=0;j<MAX_V;j++)
			{
				if ((i+j) % 4 == 3)
				{
					#if ENABLE_LOG	
					std::cout << "p:" << i <<":"<<j<<std::endl;
					#endif
					m_checkPoint.push_back(Point(i,j));
				}
			}
		}
		else if (m_step == ADD_2 )
		{
			for (int i =0;i<MAX_H;i++)
			for (int j=0; j<MAX_V;j++)
			{
				if (((i+j ) % 2 == 1) && ((i+j) % 4 != 3) && m_battle->IsUnkown(Point(i,j)))
				{
					#if ENABLE_LOG	
					std::cout << "p:" << i <<":"<<j<<std::endl;
					#endif
					m_checkPoint.push_back(Point(i,j));
				}
			}
			#if ENABLE_LOG	
			std::cout << "size:" << m_checkPoint.size() << std::endl;
			#endif
		}
	}
	else if (m_algorithm == FOUND_3_FIRST)
	{
		AddMorePoint0(m_step);
	}
	m_step ++;
}

void Utils::SetBoard(BoardType &result)
{
	BattleShipPtr battle (new BattleShip());

	SetAirCraft(battle,result[Ship::AIRCRAFT]); // put aircraft,

	//put other ships
	for (int i=Ship::BATTLE; i>= Ship::DESTROYER;i--)
	{
		result[i][0] = Ship::GetLength((Ship::TypeOfShip)i);
		ShipPtr ship;
		int x,y,r;
		do 
		{
			x = rand() % 10;
			y = rand() % 10;
			r = (rand() % 2);
			ship = ShipPtr(new Ship((Ship::TypeOfShip)i,x,y,(Ship::Rotation)r));
		} while (!battle->PutShip(ship));
		result[i][1] = x;
		result[i][2] = y;
		result[i][3] = 1 - r;
		#if ENABLE_LOG	
		std::cout << "x: " <<x<<", y: "<<y<<", r:"<<r<<std::endl;
		#endif
	}
	#if ENABLE_LOG	
	for (int i=0;i<5;i++)
	{
		std::cout << result[i][0] <<":"<<result[i][1]<<":"<<result[i][2]<<":"<<result[i][3]<<std::endl;
	}
	#endif
}

//put aircraft on battle
void Utils::SetAirCraft(BattleShipPtr battle, int shipArray [4])
{
	int type = rand() % 4;
	int x,y,r;
	ShipPtr ship;
	do {
		switch (type)
		{
		case 0:
			x = 0;
			y = rand() % 10;
			r = Ship::HORIZONTAL;
			break;
		case 1:
			x = 9;
			y = rand() % 10;
			r = Ship::HORIZONTAL;
			break;
		case 2:
			x = rand() % 10;
			y = 0;
			r = Ship::VERTICAL;
			break;
		case 3:
			x = rand() % 10;
			y = 0;
			r = Ship::VERTICAL;
			break;
		}
		ship = ShipPtr(new Ship(Ship::AIRCRAFT,x,y,(Ship::Rotation)r));
	} while (!battle->PutShip(ship));
	shipArray[0] = Ship::GetLength(Ship::AIRCRAFT);
	shipArray[1] = x;
	shipArray[2] = y;
	shipArray[3] = 1 - r;
}
//end

void Utils::AddMorePoint0(int step)
{
	if (step == INIT_STEP)
	{
		#if ENABLE_LOG	
		std::cout << "Add point for finding 5" << std::endl;
		#endif
		for (int i=0;i<MAX_H;i++)
		for (int j=0;j<MAX_V;j++)
		{
			if ((i + j) % 6 == 4)
			{
				#if ENABLE_LOG	
				std::cout << "p:" << i <<":"<<j<<std::endl;
				#endif
				m_checkPoint.push_back(Point(i,j));
			}
		}
	} 
	else if (step == ADD_3_STEP)
	{
		#if ENABLE_LOG	
		std::cout << "Add point for finding 3" << std::endl;
		#endif
		for (int i=0;i<MAX_H;i++)
		for (int j=0;j<MAX_V;j++)
		{
			if ((i + j) % 6 == 1 &&  m_battle->IsUnkown(Point(i,j)))
			{
				#if ENABLE_LOG	
				std::cout << "p:" << i <<":"<<j<<std::endl;
				#endif
				m_checkPoint.push_back(Point(i,j));
			}
		}
	}
	else if (step == ADD_2_STEP)
	{
		//old style
		for (int i=0;i<MAX_H;i++)
		for (int j=0;j<MAX_V;j++)
		{
			if ((i + j) % 3 == 2 &&  m_battle->IsUnkown(Point(i,j)))
			{
				#if ENABLE_LOG	
				std::cout << "p:" << i <<":"<<j<<std::endl;
				#endif
				m_checkPoint.push_back(Point(i,j));
			}
		}
		#if ENABLE_LOG	
		std::cout << "size:" << m_checkPoint.size() << std::endl;
		#endif

	}
}

void Utils::Swap(int &x, int &y)
{
#if ENABLE_LOG
	return;
#endif
	if (!m_swap) return;
	x = 9 - x;
}

//clean wrong point
void Utils::CleanUp()
{
	PointArray toRemove;
	for (int i = 0; i < MAX_H; i++)
	for (int j = 0; j < MAX_V; j++)
	if (m_battle->IsUnkown(Point(i,j)))
	{
		Point top,bottom,left,right;
	
		top.second = bottom.second = j;
		top.first = i - 1;
		bottom.first = i + 1;

		left.first = right.first = i;
		left.second = j - 1;
		right.second = j + 1;		
		//check top;
		while (m_battle->IsUnkown(top))
		{
			top.first--;
		}
		//check bottom;
		while (m_battle->IsUnkown(bottom))
		{
			bottom.first++;
		}
		//check left
		while (m_battle->IsUnkown(left))
		{
			left.second --;
		}
		//check right
		while (m_battle->IsUnkown(right))
		{
			right.second ++;
		}
		if ((bottom.first - top.first - 1) < m_minLength
			&& (right.second - left.second -1 ) < m_minLength)
		{
			toRemove.push_back(Point(i,j));
#if ENABLE_LOG	
			std::cout << "remove point: "<<i<<":"<<j<<std::endl;
#endif
		}
	}

	PointArray::iterator ii;
	for (ii = toRemove.begin(); ii!=toRemove.end(); ii++)
	{
		ClearPos(*ii);
	}
}

//clear a pos
void Utils::ClearPos(Point p)
{
#if ENABLE_LOG	
	std::cout << "ClearPos "<<p.first << ":"<<p.second<<std::endl;
#endif
	PointArray::iterator ii;
	for (ii=m_checkPoint.begin();ii!=m_checkPoint.end();ii++)
	{
		if (*ii == p)
		{
#if ENABLE_LOG	
			std::cout << "found!" <<std::endl;
#endif
			break;
		}
	}

	if (ii != m_checkPoint.end())
	{
		m_checkPoint.erase(ii);
	}
	// remove from pending point
	for (ii = m_pendingPoint.begin(); ii != m_pendingPoint.end(); ii++)
	{
		if (*ii == p)
		{
			break;
		}
	}

	if (ii != m_pendingPoint.end())
	{
		m_pendingPoint.erase(ii);
	}
	//end

	m_battle->SetValue(p,false);
}
//end

//recheck
void Utils::RecheckPoint()
{
	PointArray::iterator ii;
	PointArray newCheck,newPending;
	for (ii = m_checkPoint.begin(); ii != m_checkPoint.end(); ii++)
	{
		if (m_battle->IsExist(*ii,m_maxLength))
		{
			newCheck.push_back(*ii);
		}
		else
		{
			newPending.push_back(*ii);
		}
	}

	for (ii = m_pendingPoint.begin(); ii != m_pendingPoint.end(); ii++)
	{
		if (m_battle->IsExist(*ii,m_maxLength))
		{
			newCheck.push_back(*ii);
		}
		else
		{
			newPending.push_back(*ii);
		}
	}
	m_checkPoint = newCheck;
	m_pendingPoint = newPending;
#if ENABLE_LOG
	if (m_pendingPoint.size() > 0)
	{
		std::cout << "Pending size: " << m_pendingPoint.size() << std::endl;
	}
#endif
}